package org.keyroy.app.puzzle_rpg.models;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import android.graphics.Point;

public class Puzzle {
	private static final int MIN_LENGTH = 3;
	public static Tile[] tiles;

	public static void random() {
		int[] icons = new int[7];
		icons[0] = org.keyroy.app.puzzle_rpg.R.drawable.sb001;
		icons[1] = org.keyroy.app.puzzle_rpg.R.drawable.sb002;
		icons[2] = org.keyroy.app.puzzle_rpg.R.drawable.sb003;
		icons[3] = org.keyroy.app.puzzle_rpg.R.drawable.sb004;
		icons[4] = org.keyroy.app.puzzle_rpg.R.drawable.sb005;
		icons[5] = org.keyroy.app.puzzle_rpg.R.drawable.sb006;
		icons[6] = org.keyroy.app.puzzle_rpg.R.drawable.sb007;

		tiles = new Tile[icons.length];

		for (int i = 0; i < icons.length; i++) {
			tiles[i] = new Tile();
			tiles[i].ID = i;
			tiles[i].setResource(icons[i]);
		}
	}

	private static Random random = new Random();

	private static Tile randomTile() {
		return tiles[random.nextInt(tiles.length)].getCopy();
	}

	public static List<List<Point>> find(Map map) {
		Pack b = new Pack();
		b.map = map;

		Pack c = new Pack();
		c.map = map;
		List<List<Point>> lists = new ArrayList<List<Point>>();
		for (int y = 0; y < map.rows; y++) {
			for (int x = 0; x < map.columns; x++) {
				Tile tile = map.tiles[x][y];
				if (tile != null) {

					{ // check right;
						b.set(x, y);
						if (checkRight(b, tile)) {
							b.moveRight();
							c.set(b);
							if (checkRight(c, tile)) {
								c.moveRight();
								List<Point> points = create(lists, x, y, b, c);
								goRight(points, c, tile);
								x += points.size();
							}
						}

					}

					{ // check down
						b.set(x, y);
						if (checkDown(b, tile)) {
							b.moveDown();
							c.set(b);
							if (checkDown(c, tile)) {
								c.moveDown();
								List<Point> points = create(lists, x, y, b, c);
								goDown(points, c, tile);
							}
						}
					}
				}
			}
		}

		return lists;
	}

	public static List<List<Point>> tips(Map map) {
		Pack b = new Pack();
		b.map = map;

		Pack c = new Pack();
		c.map = map;

		List<List<Point>> lists = new ArrayList<List<Point>>();

		for (int y = 0; y < map.rows; y++) {
			for (int x = 0; x < map.columns; x++) {
				Tile tile = map.tiles[x][y];
				if (tile != null) {
					b.set(x, y);
					b.saveReset();
					{ // right
						b.reset();
						if (checkRight(b, tile)) {
							b.moveRight();

							c.set(b);
							c.moveRight();
							c.saveReset();

							if (isID(c, tile)) {
								List<Point> points = create(lists, x, y, b, c);
								goRight(points, c, tile);
							}
							c.reset();
							if (checkUp(c, tile)) {
								c.save();
								c.moveUp();
								List<Point> points = create(lists, x, y, b, c);
								c.restore();
								goRight(points, c, tile);
							}
							c.reset();
							if (checkRight(c, tile)) {
								c.moveRight();
								List<Point> points = create(lists, x, y, b, c);
								goRight(points, c, tile);
							}
							c.reset();
							if (checkDown(c, tile)) {
								c.save();
								c.moveDown();
								List<Point> points = create(lists, x, y, b, c);
								c.restore();
								goRight(points, c, tile);
							}

						}
					}

					{ // down
						b.reset();
						if (checkDown(b, tile)) {
							b.moveDown();

							c.set(b);
							c.moveDown();
							c.saveReset();

							if (isID(c, tile)) {
								List<Point> points = create(lists, x, y, b, c);
								goDown(points, c, tile);
							}

							c.reset();
							if (checkLeft(c, tile)) {
								c.save();
								c.moveLeft();

								List<Point> points = create(lists, x, y, b, c);
								c.restore();
								goDown(points, c, tile);
							}

							c.reset();
							if (checkDown(c, tile)) {
								c.moveDown();
								List<Point> points = create(lists, x, y, b, c);
								goDown(points, b, tile);
							}

							c.reset();
							if (checkRight(c, tile)) {
								c.save();
								c.moveRight();

								List<Point> points = create(lists, x, y, b, c);
								c.restore();
								goDown(points, c, tile);
							}
						}
					}

					{ // left down
						b.reset();
						if (checkLeftDown(b, tile)) {
							b.moveLeftDown();

							c.set(b);
							c.moveDown();
							c.saveReset();

							if (isID(c, tile)) {
								List<Point> points = create(lists, x, y, b, c);
								goDown(points, c, tile);
							}

							c.reset();
							if (checkRight(c, tile)) {
								c.moveRight();
								List<Point> points = create(lists, x, y, b, c);
								goDown(points, c, tile);
							}

							c.set(b);
							c.moveRight();
							if (checkRight(c, tile)) {
								c.moveRight();
								List<Point> points = create(lists, x, y, b, c);
								goRight(points, c, tile);
							}

						}
					}

					{ // right down
						b.reset();
						if (checkRightDown(b, tile)) {
							b.moveRightDown();

							c.set(b);
							c.moveRight();
							c.saveReset();

							if (isID(c, tile)) {
								List<Point> points = create(lists, x, y, b, c);
								goRight(points, c, tile);
							}
							c.reset();
							if (checkUp(c, tile)) {
								c.moveUp();
								List<Point> points = create(lists, x, y, b, c);
								goRight(points, c, tile);
							}

							c.set(b);
							c.moveDown();
							c.saveReset();
							if (isID(c, tile)) {
								List<Point> points = create(lists, x, y, b, c);
								goRight(points, c, tile);
							}
							c.reset();
							if (checkLeft(c, tile)) {
								c.moveLeft();
								List<Point> points = create(lists, x, y, b, c);
								goDown(points, c, tile);
							}

						}
					}

					{ // space left
						b.reset();
						b.moveRight();
						if (checkRight(b, tile)) {
							b.moveRight();

							c.set(b);
							if (checkRight(c, tile)) {
								c.moveRight();
								List<Point> points = create(lists, x, y, b, c);
								goRight(points, c, tile);
							}
						}

					}

					{ // space down
						b.reset();
						b.moveDown();
						if (checkDown(b, tile)) {
							b.moveDown();

							c.set(b);
							if (checkDown(c, tile)) {
								c.moveDown();
								List<Point> points = create(lists, x, y, b, c);
								goDown(points, c, tile);
							}
						}

					}

				}
			}
		}

		System.out.println("-----------------------");
		for (int i = 0; i < lists.size(); i++) {
			System.out.println(i + " : " + lists.get(i));
		}

		return lists;
	}

	private static final List<Point> add(List<Point> points, Pack pack) {
		points.add(new Point(pack.x, pack.y));
		return points;
	}

	private static final List<Point> add(List<Point> points, int x, int y) {
		points.add(new Point(x, y));
		return points;
	}

	private static final List<Point> create(List<List<Point>> points, int x, int y, Pack b, Pack c) {
		List<Point> list = new ArrayList<Point>();
		add(list, x, y);
		add(list, b);
		add(list, c);
		points.add(list);
		return list;
	}

	private static final void goLeft(List<Point> points, Pack pack, Tile tile) {
		while (checkLeft(pack, tile)) {
			pack.moveLeft();
			add(points, pack);
		}
	}

	private static final void goRight(List<Point> points, Pack pack, Tile tile) {
		while (checkRight(pack, tile)) {
			pack.moveRight();
			add(points, pack);
		}
	}

	private static final void goDown(List<Point> points, Pack pack, Tile tile) {
		while (checkDown(pack, tile)) {
			pack.moveDown();
			add(points, pack);
		}
	}

	private static final void goUp(List<Point> points, Pack pack, Tile tile) {
		while (checkUp(pack, tile)) {
			pack.moveUp();
			add(points, pack);
		}
	}

	public static List<List<Point>> find(Map map, Point point) {
		List<List<Point>> lists = new ArrayList<List<Point>>();
		Pack pack = new Pack();
		pack.map = map;
		pack.set(point.x, point.y);
		if (inMap(pack)) {
			Tile tile = map.tiles[point.x][point.y];
			{ // up down
				List<Point> points = new ArrayList<Point>();
				add(points, pack);
				pack.set(point.x, point.y);
				goUp(points, pack, tile);
				pack.set(point.x, point.y);
				goDown(points, pack, tile);

				if (points.size() > MIN_LENGTH) {
					lists.add(points);
				}
			}

			{ // left right
				List<Point> points = new ArrayList<Point>();
				add(points, pack);

				pack.set(point.x, point.y);
				goLeft(points, pack, tile);

				pack.set(point.x, point.y);
				goRight(points, pack, tile);

				if (points.size() > MIN_LENGTH) {
					lists.add(points);
				}
			}
		} else {
			System.out.println("find(Map , Point) : " + point);
		}
		return lists;
	}

	public static void clean(Map map, List<List<Point>> points) {
		if (points != null) {
			for (int i = 0; i < points.size(); i++) {
				List<Point> list = points.get(i);
				for (int j = 0; j < list.size(); j++) {
					Point point = list.get(j);
					if (map.tiles[point.x][point.y] != null) {
						map.tiles[point.x][point.y] = null;
					}
				}
			}
		}
	}

	public static void fill(Map map) {
		for (int x = 0; x < map.columns; x++) {
			for (int y = 0; y < map.rows; y++) {
				Tile tile = randomTile();
				tile.initLocation(map, x, y);
				tile.cx = tile.getTX();
				tile.cy = tile.getTY();

				map.tiles[x][y] = tile;
			}
		}
	}

	public static void flushLocation(Map map) {
		for (int x = 0; x < map.columns; x++) {
			for (int y = 0; y < map.rows; y++) {
				if (map.tiles[x][y] != null) {
					map.tiles[x][y].initLocation(map, x, y);

					map.tiles[x][y].cx = map.tiles[x][y].getTX();
					map.tiles[x][y].cy = map.tiles[x][y].getTY();
				}
			}
		}
	}

	public static void fix(Map map) {
		for (int x = 0; x < map.columns; x++) {
			for (int y = 0; y < map.rows; y++) {
				if (map.tiles[x][y] == null) {
					map.tiles[x][y] = randomTile();
				}
			}
		}
	}

	public static void dropDown(Map map) {
		for (int x = 0; x < map.columns; x++) {
			for (int y = map.rows - 1; y >= 0; y--) {
				if (map.tiles[x][y] == null) {
					dropDown(map, x, y);
				}
			}
		}
	}

	private static void dropDown(Map map, int x, int y) {
		if (map.tiles[x][y] == null) {
			Point point = findUpTile(map, x, y);
			if (point != null) {
				change(map, point, new Point(x, y));
			}
		}

		Point point = findUpNull(map, x, y);
		if (point != null) {
			dropDown(map, point.x, point.y);
		}
	}

	private final static Point findUpNull(Map map, int x, int y) {
		int up = y - 1;
		if (up >= 0 && up < map.rows) {
			if (map.tiles[x][up] == null) {
				return new Point(x, up);
			} else {
				return findUpNull(map, x, up);
			}
		}
		return null;
	}

	private final static Point findUpTile(Map map, int x, int y) {
		int up = y - 1;
		if (up >= 0 && up < map.rows) {
			if (map.tiles[x][up] != null) {
				return new Point(x, up);
			} else {
				return findUpTile(map, x, up);
			}
		}
		return null;
	}

	public final static void change(Map map, Point p1, Point p2) {
		if (p1 != null && p2 != null) {
			Tile t1 = map.tiles[p1.x][p1.y];
			map.tiles[p1.x][p1.y] = map.tiles[p2.x][p2.y];
			map.tiles[p2.x][p2.y] = t1;
		}
	}

	private static boolean checkLeft(Pack pack, Tile tile) {
		Tile[][] tiles = pack.map.tiles;
		int left = pack.x - 1;
		int y = pack.y;
		if (inMap(pack) && inColumns(pack.map, left) && tiles[left][y] != null && tiles[left][y].ID == tile.ID) {
			return true;
		}
		return false;
	}

	private static boolean checkRight(Pack pack, Tile tile) {
		Tile[][] tiles = pack.map.tiles;
		int right = pack.x + 1;
		int y = pack.y;
		if (inMap(pack) && inColumns(pack.map, right) && tiles[right][y] != null && tiles[right][y].ID == tile.ID) {
			return true;
		}

		return false;
	}

	private static boolean checkUp(Pack pack, Tile tile) {
		Tile[][] tiles = pack.map.tiles;
		int up = pack.y - 1;
		int x = pack.x;
		if (inMap(pack) && inRows(pack.map, up) && tiles[x][up] != null && tiles[x][up].ID == tile.ID) {
			return true;
		}
		return false;
	}

	private static boolean checkDown(Pack pack, Tile tile) {
		Tile[][] tiles = pack.map.tiles;
		int down = pack.y + 1;
		int x = pack.x;
		if (inMap(pack) && inRows(pack.map, down) && tiles[x][down] != null && tiles[x][down].ID == tile.ID) {
			return true;
		}
		return false;
	}

	private static boolean checkLeftDown(Pack pack, Tile tile) {
		Tile[][] tiles = pack.map.tiles;
		int down = pack.y + 1;
		int left = pack.x - 1;

		if (inMap(pack) && inColumns(pack.map, left) && inRows(pack.map, down) && tiles[left][down] != null
				&& tiles[left][down].ID == tile.ID) {
			return true;
		}
		return false;
	}

	private static boolean checkRightDown(Pack pack, Tile tile) {
		Tile[][] tiles = pack.map.tiles;
		int down = pack.y + 1;
		int right = pack.x + 1;

		if (inMap(pack) && inColumns(pack.map, right) && inRows(pack.map, down) && tiles[right][down] != null
				&& tiles[right][down].ID == tile.ID) {
			return true;
		}
		return false;
	}

	private static final boolean inColumns(Map map, int x) {
		return x >= 0 && x < map.columns;
	}

	private static final boolean inRows(Map map, int y) {
		return y >= 0 && y < map.rows;
	}

	private static final boolean inMap(Pack pack) {
		return inMap(pack.map, pack.x, pack.y);
	}

	public static final boolean inMap(Map map, int x, int y) {
		return inColumns(map, x) && inRows(map, y);
	}

	private static boolean isID(Pack pack, Tile tile) {
		if (inColumns(pack.map, pack.x) && inRows(pack.map, pack.y)) {
			return pack.map.tiles[pack.x][pack.y].ID == tile.ID;
		}
		return false;
	}

	private static final class Pack {
		private Map map;
		private int x, y;

		private int bx, by;
		private int rx, ry;

		public final void set(int x, int y) {
			this.x = x;
			this.y = y;
		}

		public final void save() {
			bx = x;
			by = y;
		}

		public final void saveReset() {
			rx = x;
			ry = y;
		}

		public final void restore() {
			x = bx;
			y = by;
		}

		public final void reset() {
			x = rx;
			y = ry;
		}

		public final void set(Pack pack) {
			this.x = pack.x;
			this.y = pack.y;
		}

		public final void moveUp() {
			this.y = y - 1;
		}

		public final void moveDown() {
			this.y = y + 1;
		}

		public final void moveLeft() {
			this.x = x - 1;
		}

		public final void moveRight() {
			this.x = x + 1;
		}

		public final void moveLeftDown() {
			moveLeft();
			moveDown();
		}

		public final void moveRightDown() {
			moveRight();
			moveDown();
		}

	}
}
